{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:06.984071Z",
     "start_time": "2025-01-21T06:59:06.979527Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:13.531614Z",
     "iopub.status.busy": "2025-01-21T07:49:13.531458Z",
     "iopub.status.idle": "2025-01-21T07:49:15.683916Z",
     "shell.execute_reply": "2025-01-21T07:49:15.683383Z",
     "shell.execute_reply.started": "2025-01-21T07:49:13.531594Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备\n",
    "\n",
    "https://www.kaggle.com/competitions/cifar-10/data\n",
    "\n",
    "```shell\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:15.685625Z",
     "iopub.status.busy": "2025-01-21T07:49:15.685090Z",
     "iopub.status.idle": "2025-01-21T07:49:15.804553Z",
     "shell.execute_reply": "2025-01-21T07:49:15.803927Z",
     "shell.execute_reply.started": "2025-01-21T07:49:15.685603Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "test  train\n"
     ]
    }
   ],
   "source": [
    "!ls competitions/cifar-10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:07.983879Z",
     "start_time": "2025-01-21T06:59:07.006666Z"
    },
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:15.805553Z",
     "iopub.status.busy": "2025-01-21T07:49:15.805261Z",
     "iopub.status.idle": "2025-01-21T07:49:18.092532Z",
     "shell.execute_reply": "2025-01-21T07:49:18.091993Z",
     "shell.execute_reply.started": "2025-01-21T07:49:15.805530Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(PosixPath('competitions/cifar-10/train/1.png'), 'frog'),\n",
      " (PosixPath('competitions/cifar-10/train/2.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/3.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/4.png'), 'deer'),\n",
      " (PosixPath('competitions/cifar-10/train/5.png'), 'automobile')]\n",
      "[(PosixPath('competitions/cifar-10/test/1.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/2.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/3.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/4.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/5.png'), 'cat')]\n",
      "50000 300000\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\".\")\n",
    "DATA_DIR1 =Path(\"competitions/cifar-10/\")\n",
    "train_lables_file = DATA_DIR / \"trainLabels.csv\"\n",
    "test_csv_file = DATA_DIR / \"sampleSubmission.csv\" #测试集模板csv文件\n",
    "train_folder = DATA_DIR1 / \"train\"\n",
    "test_folder = DATA_DIR1 / \"test\"\n",
    "\n",
    "#所有的类别\n",
    "class_names = [\n",
    "    'airplane',\n",
    "    'automobile',\n",
    "    'bird',\n",
    "    'cat',\n",
    "    'deer',\n",
    "    'dog',\n",
    "    'frog',\n",
    "    'horse',\n",
    "    'ship',\n",
    "    'truck',\n",
    "]\n",
    "\n",
    "def parse_csv_file(filepath, folder):\n",
    "    \"\"\"Parses csv files into (filename(path), label) format\"\"\"\n",
    "    results = []\n",
    "    #读取所有行\n",
    "    with open(filepath, 'r') as f:\n",
    "#         lines = f.readlines()  为什么加[1:]，可以试这个\n",
    "        #第一行不需要，因为第一行是标签\n",
    "        lines = f.readlines()[1:] \n",
    "    for line in lines:#依次去取每一行\n",
    "        image_id, label_str = line.strip('\\n').split(',')\n",
    "        image_full_path = folder / f\"{image_id}.png\"\n",
    "        results.append((image_full_path, label_str)) #得到对应图片的路径和分类\n",
    "    return results\n",
    "\n",
    "#解析对应的文件夹\n",
    "train_labels_info = parse_csv_file(train_lables_file, train_folder)\n",
    "test_csv_info = parse_csv_file(test_csv_file, test_folder)\n",
    "#打印\n",
    "import pprint\n",
    "pprint.pprint(train_labels_info[0:5])\n",
    "pprint.pprint(test_csv_info[0:5])\n",
    "print(len(train_labels_info), len(test_csv_info))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:08.015550Z",
     "start_time": "2025-01-21T06:59:07.983879Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.093470Z",
     "iopub.status.busy": "2025-01-21T07:49:18.093140Z",
     "iopub.status.idle": "2025-01-21T07:49:18.150932Z",
     "shell.execute_reply": "2025-01-21T07:49:18.150428Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.093450Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            filepath       class\n",
      "0  competitions/cifar-10/train/1.png        frog\n",
      "1  competitions/cifar-10/train/2.png       truck\n",
      "2  competitions/cifar-10/train/3.png       truck\n",
      "3  competitions/cifar-10/train/4.png        deer\n",
      "4  competitions/cifar-10/train/5.png  automobile\n",
      "                                filepath       class\n",
      "0  competitions/cifar-10/train/45001.png       horse\n",
      "1  competitions/cifar-10/train/45002.png  automobile\n",
      "2  competitions/cifar-10/train/45003.png        deer\n",
      "3  competitions/cifar-10/train/45004.png  automobile\n",
      "4  competitions/cifar-10/train/45005.png    airplane\n",
      "                           filepath class\n",
      "0  competitions/cifar-10/test/1.png   cat\n",
      "1  competitions/cifar-10/test/2.png   cat\n",
      "2  competitions/cifar-10/test/3.png   cat\n",
      "3  competitions/cifar-10/test/4.png   cat\n",
      "4  competitions/cifar-10/test/5.png   cat\n"
     ]
    }
   ],
   "source": [
    "# train_df = pd.DataFrame(train_labels_info)\n",
    "train_df = pd.DataFrame(train_labels_info[0:45000])\n",
    "valid_df = pd.DataFrame(train_labels_info[45000:])\n",
    "test_df = pd.DataFrame(test_csv_info)\n",
    "\n",
    "train_df.columns = ['filepath', 'class']\n",
    "valid_df.columns = ['filepath', 'class']\n",
    "test_df.columns = ['filepath', 'class']\n",
    "\n",
    "print(train_df.head())\n",
    "print(valid_df.head())\n",
    "print(test_df.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:09.221951Z",
     "start_time": "2025-01-21T06:59:08.015550Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.151697Z",
     "iopub.status.busy": "2025-01-21T07:49:18.151518Z",
     "iopub.status.idle": "2025-01-21T07:49:18.951323Z",
     "shell.execute_reply": "2025-01-21T07:49:18.950790Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.151678Z"
    }
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms\n",
    "\n",
    "class Cifar10Dataset(Dataset):\n",
    "    df_map = {\n",
    "        \"train\": train_df,\n",
    "        \"eval\": valid_df,\n",
    "        \"test\": test_df\n",
    "    }\n",
    "    label_to_idx = {label: idx for idx, label in enumerate(class_names)}\n",
    "    idx_to_label = {idx: label for idx, label in enumerate(class_names)}\n",
    "    def __init__(self, mode, transform=None):\n",
    "        self.df = self.df_map.get(mode, None)\n",
    "        if self.df is None:\n",
    "            raise ValueError(\"mode should be one of train, val, test, but got {}\".format(mode))\n",
    "\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        img_path, label = self.df.iloc[index]\n",
    "        img = Image.open(img_path).convert('RGB')\n",
    "        # # img 转换为 channel first\n",
    "        # img = img.transpose((2, 0, 1))\n",
    "        # transform\n",
    "        img = self.transform(img)\n",
    "        # label 转换为 idx\n",
    "        label = self.label_to_idx[label]\n",
    "        return img, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.df.shape[0]\n",
    "    \n",
    "IMAGE_SIZE = 32\n",
    "mean, std = [0.4914, 0.4822, 0.4465], [0.247, 0.243, 0.261]\n",
    "\n",
    "transforms_train = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        # random rotation 40\n",
    "        transforms.RandomRotation(40),\n",
    "        # horizaontal flip\n",
    "        transforms.RandomHorizontalFlip(),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "transforms_eval = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "train_ds = Cifar10Dataset(\"train\", transforms_train)\n",
    "eval_ds = Cifar10Dataset(\"eval\", transforms_eval) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:09.225457Z",
     "start_time": "2025-01-21T06:59:09.221951Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.952469Z",
     "iopub.status.busy": "2025-01-21T07:49:18.951930Z",
     "iopub.status.idle": "2025-01-21T07:49:18.955482Z",
     "shell.execute_reply": "2025-01-21T07:49:18.954918Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.952446Z"
    }
   },
   "outputs": [],
   "source": [
    "batch_size = 128\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)   \n",
    "eval_dl = DataLoader(eval_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T06:59:09.235243Z",
     "start_time": "2025-01-21T06:59:09.225966Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.957606Z",
     "iopub.status.busy": "2025-01-21T07:49:18.957295Z",
     "iopub.status.idle": "2025-01-21T07:49:18.959964Z",
     "shell.execute_reply": "2025-01-21T07:49:18.959495Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.957586Z"
    }
   },
   "outputs": [],
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "# def cal_mean_std(ds):\n",
    "#     mean = 0.\n",
    "#     std = 0.\n",
    "#     for img, _ in ds:\n",
    "#         mean += img.mean(dim=(1, 2))\n",
    "#         std += img.std(dim=(1, 2))\n",
    "#     mean /= len(ds)\n",
    "#     std /= len(ds)\n",
    "#     return mean, std\n",
    "#\n",
    "# # 经过 normalize 后 均值为0，方差为1\n",
    "# print(cal_mean_std(train_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:44:09.956439Z",
     "start_time": "2025-01-21T07:44:09.952969Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.960631Z",
     "iopub.status.busy": "2025-01-21T07:49:18.960461Z",
     "iopub.status.idle": "2025-01-21T07:49:18.966729Z",
     "shell.execute_reply": "2025-01-21T07:49:18.966267Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.960612Z"
    }
   },
   "outputs": [],
   "source": [
    "class Resdiual(nn.Module):\n",
    "    \"\"\"浅层的残差块，无bottleneck（性能限制）\"\"\"\n",
    "    def __init__(self, input_channels, output_channels, use_1x1conv=False, stride=1):\n",
    "        \"\"\"\n",
    "        残差块\n",
    "        params filters: 过滤器数目，决定输出通道\n",
    "        params use_1x1conv: 是否使用 1x1 卷积，此时 stride=2，进行降采样\n",
    "        params strides: 步长，默认为1，当降采样的时候设置为2\n",
    "        \"\"\"\n",
    "        super().__init__()\n",
    "        self.conv1 = nn.Conv2d(\n",
    "            in_channels=input_channels,\n",
    "            out_channels=output_channels,\n",
    "            kernel_size=3,\n",
    "            stride=stride,\n",
    "            padding=1,\n",
    "        )\n",
    "        self.conv2 = nn.Conv2d(\n",
    "            in_channels=output_channels,\n",
    "            out_channels=output_channels,\n",
    "            kernel_size=3,\n",
    "            stride=1,\n",
    "            padding=1,\n",
    "        )  \n",
    "        if use_1x1conv:\n",
    "            # skip connection 的 1x1 卷积，用于改变通道数和降采样，使得最终可以做残差连接\n",
    "            self.conv_sc = nn.Conv2d(\n",
    "                in_channels=input_channels,\n",
    "                out_channels=output_channels,\n",
    "                kernel_size=1,\n",
    "                stride=stride,\n",
    "            )\n",
    "        else:\n",
    "            self.conv_sc = None\n",
    "        \n",
    "        self.bn1 = nn.BatchNorm2d(output_channels, eps=1e-5, momentum=0.9)\n",
    "        self.bn2 = nn.BatchNorm2d(output_channels, eps=1e-5, momentum=0.9)\n",
    "        \n",
    "    def forward(self, inputs):\n",
    "        \"\"\"forward\"\"\"\n",
    "        flow = F.relu(self.bn1(self.conv1(inputs))) #卷积->BN->ReLU\n",
    "        flow = self.bn2(self.conv2(flow)) #卷积->BN\n",
    "        # print(f'flow shape: {flow.shape}')\n",
    "        if self.conv_sc:#如果有1x1卷积，就用1x1卷积\n",
    "            inputs = self.conv_sc(inputs)\n",
    "        # print(f'inputs shape: {inputs.shape}')\n",
    "        return F.relu(flow + inputs) #残差连接->ReLU，必须保证flow和inputs的shape相同\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:44:13.122065Z",
     "start_time": "2025-01-21T07:44:13.118991Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.967457Z",
     "iopub.status.busy": "2025-01-21T07:49:18.967284Z",
     "iopub.status.idle": "2025-01-21T07:49:18.971915Z",
     "shell.execute_reply": "2025-01-21T07:49:18.971404Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.967439Z"
    }
   },
   "outputs": [],
   "source": [
    "class ResdiualBlock(nn.Module):\n",
    "    \"\"\"若干个 Resdiual 模块堆叠在一起，通常在第一个模块给 skip connection 使用 1x1conv with stride=2\"\"\"\n",
    "    def __init__(self, input_channels, output_channels, num, is_first=False):\n",
    "        \"\"\"\n",
    "        params filters: 过滤器数目\n",
    "        params num: 堆叠几个 Resdiual 模块\n",
    "        params is_first: 是不是第一个block。 最上面一层 Resdiual 的 stride=1,is_first=False,图像尺寸减半，False图像尺寸不变\n",
    "        \"\"\"\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential() # 用于存放 Resdiual 模块\n",
    "        self.model.append(Resdiual(\n",
    "            input_channels=input_channels, \n",
    "            output_channels=output_channels, \n",
    "            use_1x1conv=not is_first, \n",
    "            stride=1 if is_first else 2\n",
    "            )) # 第一个 Resdiual 模块，负责通道翻倍,图像的尺寸减半\n",
    "        for _ in range(1, num):\n",
    "            # 堆叠 num 个 Resdiual 模块\n",
    "            self.model.append(Resdiual(\n",
    "                input_channels=output_channels, \n",
    "                output_channels=output_channels,\n",
    "                use_1x1conv=False, stride=1\n",
    "                ))\n",
    "    def forward(self, inputs):\n",
    "        return self.model(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:44:20.834700Z",
     "start_time": "2025-01-21T07:44:20.831200Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.972599Z",
     "iopub.status.busy": "2025-01-21T07:49:18.972436Z",
     "iopub.status.idle": "2025-01-21T07:49:18.978413Z",
     "shell.execute_reply": "2025-01-21T07:49:18.977940Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.972581Z"
    }
   },
   "outputs": [],
   "source": [
    "class ResNetForCifar10(nn.Module):\n",
    "    def __init__(self, n=3, num_classes=10):\n",
    "        \"\"\"\n",
    "        params units: 预测类别的数目\n",
    "        \"\"\"\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential(\n",
    "            nn.Conv2d(\n",
    "                in_channels=3,\n",
    "                out_channels=16,\n",
    "                kernel_size=3,\n",
    "                stride=1,\n",
    "            ),  # conv1\n",
    "            nn.BatchNorm2d(16, momentum=0.9, eps=1e-5),\n",
    "            nn.ReLU(),\n",
    "            ResdiualBlock(input_channels=16, output_channels=16, num=2*n, is_first=True),  # conv2_x\n",
    "            ResdiualBlock(input_channels=16, output_channels=32, num=2*n),  # conv3_x\n",
    "            ResdiualBlock(input_channels=32, output_channels=64, num=2*n),  # conv4_x\n",
    "            # average pool\n",
    "            nn.AdaptiveAvgPool2d((1, 1)), #无论输入图片大小，输出都是1x1，把width和height压缩为1\n",
    "            nn.Flatten(),\n",
    "            # fully connected\n",
    "            nn.Linear(in_features=64, out_features=num_classes),\n",
    "            )\n",
    "        \n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 kaiming 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
    "                nn.init.kaiming_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "        \n",
    "    def forward(self, inputs):\n",
    "        return self.model(inputs)\n",
    "\n",
    "\n",
    "# for key, value in ResNetForCifar10(num_classes=len(class_names)).named_parameters():\n",
    "#     print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:42:15.755683Z",
     "start_time": "2025-01-21T07:42:15.746118Z"
    },
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:18.979131Z",
     "iopub.status.busy": "2025-01-21T07:49:18.978952Z",
     "iopub.status.idle": "2025-01-21T07:49:18.999320Z",
     "shell.execute_reply": "2025-01-21T07:49:18.998836Z",
     "shell.execute_reply.started": "2025-01-21T07:49:18.979112Z"
    },
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total trainable parameters: 565386\n"
     ]
    }
   ],
   "source": [
    "#模型总参数量\n",
    "total_params = sum(p.numel() for p in ResNetForCifar10(num_classes=len(class_names)).parameters() if p.requires_grad)\n",
    "print(f\"Total trainable parameters: {total_params}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-21T07:44:30.716019Z",
     "start_time": "2025-01-21T07:44:30.689319Z"
    },
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:19.000119Z",
     "iopub.status.busy": "2025-01-21T07:49:18.999844Z",
     "iopub.status.idle": "2025-01-21T07:49:19.027221Z",
     "shell.execute_reply": "2025-01-21T07:49:19.026707Z",
     "shell.execute_reply.started": "2025-01-21T07:49:19.000099Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 10])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#随机一个张量，喂给上面的model\n",
    "x = torch.randn(1, 3, 32, 32)\n",
    "model = ResNetForCifar10(num_classes=len(class_names))\n",
    "model(x).shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:19.028052Z",
     "iopub.status.busy": "2025-01-21T07:49:19.027805Z",
     "iopub.status.idle": "2025-01-21T07:49:19.054554Z",
     "shell.execute_reply": "2025-01-21T07:49:19.054015Z",
     "shell.execute_reply.started": "2025-01-21T07:49:19.028032Z"
    }
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:19.055460Z",
     "iopub.status.busy": "2025-01-21T07:49:19.055126Z",
     "iopub.status.idle": "2025-01-21T07:49:19.114118Z",
     "shell.execute_reply": "2025-01-21T07:49:19.113650Z",
     "shell.execute_reply.started": "2025-01-21T07:49:19.055439Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:19.114969Z",
     "iopub.status.busy": "2025-01-21T07:49:19.114668Z",
     "iopub.status.idle": "2025-01-21T07:49:19.119853Z",
     "shell.execute_reply": "2025-01-21T07:49:19.119425Z",
     "shell.execute_reply.started": "2025-01-21T07:49:19.114949Z"
    }
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:19.120813Z",
     "iopub.status.busy": "2025-01-21T07:49:19.120623Z",
     "iopub.status.idle": "2025-01-21T07:49:19.124733Z",
     "shell.execute_reply": "2025-01-21T07:49:19.124248Z",
     "shell.execute_reply.started": "2025-01-21T07:49:19.120793Z"
    }
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:49:19.125647Z",
     "iopub.status.busy": "2025-01-21T07:49:19.125474Z",
     "iopub.status.idle": "2025-01-21T07:52:18.691304Z",
     "shell.execute_reply": "2025-01-21T07:52:18.690721Z",
     "shell.execute_reply.started": "2025-01-21T07:49:19.125629Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 7040/7040 [02:58<00:00, 39.37it/s, epoch=19]\n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "model = ResNetForCifar10(num_classes=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 Rmsprop\n",
    "# Optimizers specified in the torch.optim package\n",
    "# >>> # Assuming optimizer uses lr = 0.05 for all groups\n",
    "# >>> # lr = 0.05     if epoch < 30\n",
    "# >>> # lr = 0.005    if 30 <= epoch < 80\n",
    "# >>> # lr = 0.0005   if epoch >= 80\n",
    "# >>> scheduler = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)\n",
    "\n",
    "class OptimizerWithScheduler:\n",
    "    def __init__(self, parameters, lr, momentum, weight_decay):\n",
    "        self.optimizer = torch.optim.SGD(parameters, lr=lr, momentum=momentum, weight_decay=weight_decay) # 优化器\n",
    "        self.scheduler = torch.optim.lr_scheduler.MultiStepLR(self.optimizer, milestones=[32_000, 48_000], gamma=0.1) # 学习率衰减\n",
    "        \n",
    "    def step(self):\n",
    "        self.optimizer.step()\n",
    "        self.scheduler.step()\n",
    "        \n",
    "    @property\n",
    "    def param_groups(self):\n",
    "        return self.optimizer.param_groups\n",
    "        \n",
    "    def zero_grad(self):\n",
    "        self.optimizer.zero_grad()\n",
    "        \n",
    "optimizer = OptimizerWithScheduler(model.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "    \n",
    "exp_name = \"resnet34\"\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/{exp_name}\")\n",
    "tensorboard_callback.draw_model(model, [1, 3, IMAGE_SIZE, IMAGE_SIZE])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(f\"checkpoints/{exp_name}\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_dl, \n",
    "    eval_dl, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_dl)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:52:18.692335Z",
     "iopub.status.busy": "2025-01-21T07:52:18.692058Z",
     "iopub.status.idle": "2025-01-21T07:52:18.935996Z",
     "shell.execute_reply": "2025-01-21T07:52:18.935485Z",
     "shell.execute_reply.started": "2025-01-21T07:52:18.692312Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0MAAAHACAYAAABge7OwAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA1ndJREFUeJzs3Xd8VGXWwPHfnZpOqAk99N4EQRAVpaOsYu+KveCrsq7K2gBdWRuia2HXFV17WcvuKiJFsQCCgPQivSahk54p975/TGYy5U7NpAw5389HyczccmaSSe6Z8zznUTRN0xBCCCGEEEKIesZQ2wEIIYQQQgghRG2QZEgIIYQQQghRL0kyJIQQQgghhKiXJBkSQgghhBBC1EuSDAkhhBBCCCHqJUmGhBBCCCGEEPWSJENCCCGEEEKIekmSISGEEEIIIUS9ZKrtAOJBVVUOHjxIeno6iqLUdjhCCFGvaJpGYWEhLVq0wGCQz9jc5G+TEELUjmj+Lp0SydDBgwdp3bp1bYchhBD12r59+2jVqlVth1FnyN8mIYSoXZH8XTolkqH09HTA9YQzMjKi3t9utzN//nxGjRqF2WyOd3jVRuKuOYkYMyRm3IkYMyRm3PGKuaCggNatW3t+FwsX+duUOHEnYsyQmHEnYswgcdekeMQczd+lUyIZcg8/yMjIiPkPTkpKChkZGQnzgwISd01KxJghMeNOxJghMeOOd8wyFMyX/G1KnLgTMWZIzLgTMWaQuGtSPGOO5O+SDO4WQgghhBBC1EuSDAkhhBBCCCHqJUmGhBBCCCGEEPXSKTFnSAhRdzmdTux2e8Tb2+12TCYTZWVlOJ3OaowsvhIx7khjNhqNmEwmmRNUDTRNw+Fw6L7+ifgzBYkZdywxy/tCiFND1MnQjz/+yHPPPceqVavIzc3liy++4KKLLvI8HuyXwrPPPsuf/vQn3cemTp3KtGnTfO7r0qULW7ZsiTY8IUQdUlRUxP79+9E0LeJ9NE0jOzubffv2JdRFRiLGHU3MKSkpNG/eHIvFUkPRnfpsNhu5ubmUlJToPp6IP1OQmHHHGrO8L4RIfFEnQ8XFxfTp04ebbrqJiy++OODx3Nxcn9vffPMNN998M5dccknI4/bo0YOFCxdWBmaSopUQiczpdLJ//35SUlJo2rRpxBcYqqpSVFREWlpaQi3gmYhxRxKzpmnYbDYOHz7Mrl276NSpU8I8v7pMVVV27dqF0WikRYsWWCyWgPdIIv5MQWLGHW3M8r4Q4tQRdcYxduxYxo4dG/Tx7Oxsn9v/+c9/OPfcc2nfvn3oQEymgH2FEInLbrejaRpNmzYlOTk54v1UVcVms5GUlJRQFxeJGHekMScnJ2M2m9mzZ49ne1E1NpsNVVVp3bo1KSkputsk4s8UJGbcscQs7wshTg3V+lsqPz+fr7/+mptvvjnsttu2baNFixa0b9+ea665hr1791ZnaEKIGpIow2REaIlyUZto5HVNbPL9EyLxVetYtH/961+kp6frDqfzNmjQIN5++226dOlCbm4u06ZN46yzzmLDhg26K8eWl5dTXl7uuV1QUAC4PomOZqK2m3ufWPatTRJ3zUnEmKF243ZXhlRVRVXViPdzzy9y75soEjHuaGJWVRVN07Db7RiNRp/HEu19IYQQQrhVazI0Z84crrnmmrClY+9hd71792bQoEG0bduWTz75RLeqNGPGjICGCwDz588POtwgEgsWLIh539okcdecRIwZaidu99DXoqIibDZb1PsXFhZWQ1TVLxHjjiRmm81GaWkpP/74Iw6Hw+exYA0AhBBCiLqu2pKhn376ia1bt/Lxxx9HvW9mZiadO3dm+/btuo9PmTKFyZMne24XFBTQunVrRo0aRUZGRtTns9vtLFiwgJEjR2I2m6Pev7ZI3DUnEWOG2o27rKyMffv2kZaWFtVYek3TKCwsJD09PaGG2OnF3b59e+69917uvffeKh9/8eLFDB8+nKNHj5KZmVnl40F0r3VZWRnJycmcffbZAd9Pd3VeiGjl5ORw3333cd9999V2KEKIeqrakqE333yT/v3706dPn6j3LSoqYseOHVx33XW6j1utVqxWa8D9ZrO5Shd8Vd2/tkjcNScRY4baidvpdKIoCgaDIapx9e7hWu59a9qwYcPo27cvs2bNimo/vbh//fVXUlNT4/I83MeI9vUMJZrX2mAwoCiK7s9SIr4nROxifY/ocb9HhBCitkT9F7WoqIg1a9awZs0aAHbt2sWaNWt8Gh4UFBTw6aefcsstt+geY/jw4bzyyiue2w888AA//PADu3fvZunSpUyYMAGj0chVV10VbXhCCFGt3ItkRqJp06ZVGrorRCKS94gQIpFEnQytXLmSfv360a9fPwAmT55Mv379ePzxxz3b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2LBh1jZr167FZDJZ26xYsYLIyEi8vb2bHZdSimnTpvHtt9/yyy+/EBERYbM/NjYWR0dHm7hTUlJIT0+3iXvnzp02P5wrVqzAw8PD+kMfHx9vc47aNi35b2M2m6moqOiwMY8ePZqdO3eybds261dcXByTJk2yPu6IcZ+quLiYgwcPEhgY2GGv9YgRIxqU4d23bx9hYWFAx/15rLVgwQL8/Py4+uqrrds66rUWpyd9U+fumzp6vwTSN0nfdPbsom86t5oQF45FixYpo9GoFi5cqHbv3q3uuece5eXlZVO9ojUVFRWprVu3qq1btypAvfTSS2rr1q3q8OHDSilLuUQvLy/13XffqR07dqjrrruu0XKJgwYNUhs2bFC//vqr6tWrl025xPz8fOXv769uvfVWlZycrBYtWqRcXFzOuVzifffdpzw9PVViYqJN2cTS0lJrm3vvvVeFhoaqX375RW3atEnFx8er+Ph46/7akoljxoxR27ZtU8uWLVNdu3ZttGTizJkz1Z49e9S8efPOqzzlww8/rNasWaMOHTqkduzYoR5++GGlaZpavnx5h425MfUr9nTUuP/xj3+oxMREdejQIfXbb7+phIQE5evrq7KysjpszBs3blQODg7qmWeeUfv371effPKJcnFxUR9//LG1TUf8eVTKUmksNDRUPfTQQw32dcRrLc5M+qbms8e+6ULpl5SSvqm1Ypa+qfWvdadPhpRS6vXXX1ehoaHKYDCooUOHqt9//73NXnv16tUKaPA1efJkpZSlZOJjjz2m/P39ldFoVKNHj1YpKSk25zh58qSaOHGicnNzUx4eHmrKlCmqqKjIps327dvVyJEjldFoVN26dVNz584955gbixdQCxYssLYpKytT999/v/L29lYuLi7qz3/+szpx4oTNedLS0tSVV16pnJ2dla+vr/rHP/6hTCZTg+szcOBAZTAYVPfu3W1eo7nuuOMOFRYWpgwGg+ratasaPXq0tcPpqDE35tQOpyPGfdNNN6nAwEBlMBhUt27d1E033WSzJkJHjFkppX744QfVv39/ZTQaVVRUlHrnnXds9nfEn0ellPr5558V0CAWpTrutRZnJn1T89hj33Sh9EtKSd/UWjErJX1Ta19rTSmlmjeWJIQQQgghhBD2r1PfMySEEEIIIYTovCQZEkIIIYQQQnRKkgwJIYQQQgghOiVJhoQQQgghhBCdkiRDQgghhBBCiE5JkiEhhBBCCCFEpyTJkBBCCCGEEKJTkmRICCGEEEII0SlJMiREG7n99tsZP358e4chhBBCWEnfJDo7SYaEEEIIIYQQnZIkQ0K0sK+++oro6GicnZ3p0qULCQkJzJw5kw8++IDvvvsOTdPQNI3ExEQAjhw5wo033oiXlxc+Pj5cd911pKWlWc9X+1e7J598kq5du+Lh4cG9995LZWVl+7xBIYQQdkf6JiEa59DeAQhxITlx4gQTJ07kueee489//jNFRUWsW7eO2267jfT0dAoLC1mwYAEAPj4+mEwmxo4dS3x8POvWrcPBwYGnn36acePGsWPHDgwGAwCrVq3CycmJxMRE0tLSmDJlCl26dOGZZ55pz7crhBDCDkjfJETTJBkSogWdOHGCqqoq/vKXvxAWFgZAdHQ0AM7OzlRUVBAQEGBt//HHH2M2m3nvvffQNA2ABQsW4OXlRWJiImPGjAHAYDAwf/58XFxc6NevH//5z3+YOXMmTz31FDqdDPAKIYRomvRNQjRN/qcK0YJiYmIYPXo00dHRTJgwgXfffZe8vLwm22/fvp0DBw7g7u6Om5sbbm5u+Pj4UF5ezsGDB23O6+LiYn0eHx9PcXExR44cadX3I4QQwv5J3yRE02RkSIgWpNfrWbFiBevXr2f58uW8/vrrPProo2zYsKHR9sXFxcTGxvLJJ5802Ne1a9fWDlcIIUQnIH2TEE2TZEiIFqZpGiNGjGDEiBE8/vjjhIWF8e2332IwGKiurrZpO3jwYD7//HP8/Pzw8PBo8pzbt2+nrKwMZ2dnAH7//Xfc3NwICQlp1fcihBDiwiB9kxCNk2lyQrSgDRs28Oyzz7Jp0ybS09P55ptvyM7Opk+fPoSHh7Njxw5SUlLIycnBZDIxadIkfH19ue6661i3bh2HDh0iMTGRBx54gKNHj1rPW1lZyZ133snu3btZunQps2fPZtq0aTInWwghxBlJ3yRE02RkSIgW5OHhwdq1a3nllVcoLCwkLCyMF198kSuvvJK4uDgSExOJi4ujuLiY1atXc+mll7J27Voeeugh/vKXv1BUVES3bt0YPXq0zV/jRo8eTa9evRg1ahQVFRVMnDiRJ554ov3eqBBCCLshfZMQTdOUUqq9gxBCNO32228nPz+fxYsXt3coQgghBCB9k7hwyDimEEIIIYQQolOSZEgIIYQQQgjRKck0OSGEEEIIIUSnJCNDQgghhBBCiE5JkiEhhBBCCCFEpyTJkBBCCCGEEKJTkmRICCGEEEII0SlJMiSEEEIIIYTolCQZEkIIIYQQQnRKkgwJIYQQQgghOiVJhoQQQgghhBCdkiRDQgghhBBCiE7p/wFNcvuntds/dQAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:52:18.936964Z",
     "iopub.status.busy": "2025-01-21T07:52:18.936654Z",
     "iopub.status.idle": "2025-01-21T07:52:19.662789Z",
     "shell.execute_reply": "2025-01-21T07:52:19.662194Z",
     "shell.execute_reply.started": "2025-01-21T07:52:18.936944Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_2440/3500857114.py:4: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
      "  model.load_state_dict(torch.load(f\"checkpoints/{exp_name}/best.ckpt\", map_location=\"cpu\"))\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.6683\n",
      "accuracy: 0.7760\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(f\"checkpoints/{exp_name}/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, eval_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 推理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:52:19.663752Z",
     "iopub.status.busy": "2025-01-21T07:52:19.663487Z",
     "iopub.status.idle": "2025-01-21T07:54:19.176801Z",
     "shell.execute_reply": "2025-01-21T07:54:19.176329Z",
     "shell.execute_reply.started": "2025-01-21T07:52:19.663728Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2344/2344 [01:59<00:00, 19.62it/s]\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>filepath</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>competitions/cifar-10/test/1.png</td>\n",
       "      <td>deer</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>competitions/cifar-10/test/2.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>competitions/cifar-10/test/3.png</td>\n",
       "      <td>automobile</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>competitions/cifar-10/test/4.png</td>\n",
       "      <td>ship</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>competitions/cifar-10/test/5.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                           filepath       class\n",
       "0  competitions/cifar-10/test/1.png        deer\n",
       "1  competitions/cifar-10/test/2.png    airplane\n",
       "2  competitions/cifar-10/test/3.png  automobile\n",
       "3  competitions/cifar-10/test/4.png        ship\n",
       "4  competitions/cifar-10/test/5.png    airplane"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# test_df\n",
    "test_ds = Cifar10Dataset(\"test\", transform=transforms_eval)\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, drop_last=False)\n",
    "\n",
    "preds_collect = []\n",
    "model.eval()\n",
    "for data, fake_label in tqdm(test_dl):\n",
    "    data = data.to(device=device)\n",
    "    logits = model(data)\n",
    "    preds = [test_ds.idx_to_label[idx] for idx in logits.argmax(axis=-1).cpu().tolist()]\n",
    "    preds_collect.extend(preds)\n",
    "    \n",
    "test_df[\"class\"] = preds_collect\n",
    "test_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-01-21T07:54:19.177784Z",
     "iopub.status.busy": "2025-01-21T07:54:19.177388Z",
     "iopub.status.idle": "2025-01-21T07:54:19.494510Z",
     "shell.execute_reply": "2025-01-21T07:54:19.493915Z",
     "shell.execute_reply.started": "2025-01-21T07:54:19.177764Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# 导出 submission.csv\n",
    "test_df.to_csv(\"submission.csv\", index=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
